using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using DirkRettschlag.Utils;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace Departure
{
    public partial class ctrlStation : UserControl
    {
        #region Delegates
        
        private delegate void RequestDelegate(string Source, bool Error);
        private delegate void BuildDataDelegate(string Time, string Train, string Direction, string Platform, string Later, string FullDateTime, string Error, bool isError, bool done, bool update, int currentitem, int allitems);
        public delegate void SetRefreshButtonEventDelegate(string internalName, bool enabled);
        public delegate void SetCountdownEventDelegate(string countdownTime, string train, string later);

        #endregion Delegates

        #region Events

        public event SetRefreshButtonEventDelegate OnSetRefreshButton;
        public event SetCountdownEventDelegate OnSetCountdown;

        #endregion Events

        #region Variables

        StationSettings stationSettings;
        GeneralSettings generalSettings;
        bool isUpdating;
        DataTable dt;

        public Webservice.WebserviceWrapper ws;
        #endregion Variables

        #region Properties

        public StationSettings StationSettings
        {
            get { return this.stationSettings; }
            set { this.stationSettings = value; }
        }

        public GeneralSettings GeneralSettings
        {
            set { this.generalSettings = value; }
        }

        public bool IsUpdating
        {
            get { return this.isUpdating; }
        }

        #endregion Properties

        #region Constructor
        public ctrlStation(string internalName, GeneralSettings generalSettings)
        {
            InitializeComponent();

            stationSettings = new StationSettings(internalName);

            stationSettings.getSettings();

            this.generalSettings = generalSettings;

            ws = new Departure.Webservice.WebserviceWrapper();

            dt = new DataTable("Departure");

            dt.Columns.Add("Time", typeof(string));
            dt.Columns.Add("Train", typeof(string));
            dt.Columns.Add("Direction", typeof(string));
            dt.Columns.Add("Platform", typeof(string));
            dt.Columns.Add("Later", typeof(string));
            dt.Columns.Add("Full_DateTime", typeof(string));

        }
        #endregion Constructor

        #region Public Methods

        public void RefreshDepartures()
        {
            if (Utilities.Connected() || generalSettings.AutoConnect)
            {
                Cursor.Current = Cursors.WaitCursor;

                this.panelStationItems.Visible = false;
                this.panelStationItems.SuspendLayout();

                if (OnSetRefreshButton != null)
                    OnSetRefreshButton(this.Name, false);

                labelProgressStation.Text = "Retrieving Data...";
                labelProgressStation.Visible = true;
                lock (this.panelStationItems)
                {
                    for (int i = this.panelStationItems.Controls.Count - 1; i >= 0; i--)
                    {
                        if (this.panelStationItems.Controls[i].GetType() == typeof(ctrlStationItem))
                        {
                            this.panelStationItems.Controls.RemoveAt(i);
                        }
                    }
                }

                isUpdating = true;

                GetData();
            }
        }

        public void RemovePastDepartures()
        {
            try
            {
                for (int i = Invoking.GetControlsCount(this.panelStationItems) - 1; i >= 0; i--)
                {
                    if (Invoking.GetControlAtIndex(this.panelStationItems, i).GetType() == typeof(ctrlStationItem))
                    {
                        double correction = 0;
                        string later = Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "Later");
                        if (later != "" && later.IndexOf("approx. ") >= 0)
                            correction = double.Parse(later.Replace("approx. ", "").Replace(" minutes later", ""));

                        DateTime corrected = DateTime.FromFileTimeUtc(long.Parse(Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "FullDateTime"))).AddMinutes(correction);
                        if (corrected < DateTime.Now)
                        {
                            Invoking.HideControlAtIndex(this.panelStationItems, i);
                            Invoking.RemoveControlAtIndex(this.panelStationItems, i);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
            }

        }

        public void SaveDepartures()
        {
            try
            {
                dt.Rows.Clear();
                for (int i = Invoking.GetControlsCount(this.panelStationItems) - 1; i >= 0; i--)
                {
                    if (Invoking.GetControlAtIndex(this.panelStationItems, i).GetType() == typeof(ctrlStationItem))
                    {
                        DataRow dr = dt.NewRow();
                        dr["Time"] = Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "Time");
                        dr["Train"] = Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "Train");
                        dr["Direction"] = Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "Direction");
                        dr["Platform"] = Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "Platform");
                        dr["Later"] = Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "Later");
                        dr["Full_DateTime"] = Invoking.GetStringProperty(((ctrlStationItem)Invoking.GetControlAtIndex(this.panelStationItems, i)), "FullDateTime");

                        dt.Rows.Add(dr);
                    }
                }
                dt.WriteXml(Utilities.AppPath() + "\\" + this.Name + ".xml");
            }
            catch (Exception ex)
            {
            }

        }

        public void LoadDepartures()
        {
            if (!backgroundWorkerLoad.IsBusy)
                backgroundWorkerLoad.RunWorkerAsync();
        }

        #endregion Public Methods

        #region Private Methods

        private void LoadDeparturesThread()
        {
            isUpdating = true;
            Invoking.SetCursor(this, Cursors.WaitCursor);
            Invoking.SetControlVisible(this.panelStationItems, false);
            Invoking.SetControlLayoutMode(this.panelStationItems, true);

            if (System.IO.File.Exists(Utilities.AppPath() + "\\" + this.Name + ".xml"))
            {
                dt.Rows.Clear();
                dt.ReadXml(Utilities.AppPath() + "\\" + this.Name + ".xml");

                foreach (DataRow dr in dt.Rows)
                {
                    AddStationItem(dr["Time"].ToString(), dr["Train"].ToString(), dr["Direction"].ToString(), dr["Platform"].ToString(), dr["Later"].ToString(), dr["Full_DateTime"].ToString());
                }
            }
            Invoking.SetControlLayoutMode(this.panelStationItems, false);
            Invoking.SetControlVisible(this.panelStationItems, true);
            Invoking.SetCursor(this, Cursors.Default);
            
            isUpdating = false;
            
            if (OnSetRefreshButton != null)
                OnSetRefreshButton(this.Name, true);
        }
        
        private void GetData()
        {

            if (generalSettings.UseWebService)
            {
                //Departure.Webservice.Methods.GetDeparturesInput input = new Departure.Webservice.Methods.GetDeparturesInput();

                //input.Station = stationSettings.Station;
                //input.Transfers = stationSettings.Transfers;

                //input.TransfersSelected = stationSettings.TransfersSelected;

                //input.UseTrainsFilter = stationSettings.UseTrainsFilter;
                //input.TrainsFilter = stationSettings.TrainsFilter;

                //input.UseDirectionsFilter = stationSettings.UseDirectionsFilter;
                //input.DirectionsFilter = stationSettings.DirectionsFilter;

                //input.UsePlatformsFilter = stationSettings.UsePlatformsFilter;
                //input.PlatformsFilter = stationSettings.PlatformsFilter;

                //ws.setURL(generalSettings.WebServiceURL);

                //Invoking.SetControlVisible(this.panelStationItems, false);
                //Invoking.SetControlLayoutMode(this.panelStationItems, true);

                //ws.BeginGetDepartures(generalSettings.DeviceID, input, GetDeparturesCallback, ws);

                Thread t = new Thread(new ThreadStart(GetDeparturesThread));
                t.IsBackground = true; //make them a daemon - prevent thread callback issues
                t.Start();
            }
            else
            {
                string URL = "";
                if (stationSettings.StationID == "")
                    URL = "http://reiseauskunft.bahn.de/bin/bhftafel.exe/en?L=ua_pda&input=" + stationSettings.Station + "&time=actual&boardType=dep&productsFilter=" + stationSettings.Transfers + "&start=Anzeigen&distance=1";
                else
                    URL = "http://reiseauskunft.bahn.de/bin/bhftafel.exe/en?L=ua_pda&input=" + stationSettings.StationID + "&time=actual&boardType=dep&productsFilter=" + stationSettings.Transfers + "&start=Anzeigen&distance=1";

                RequestDelegate requestDone = new RequestDelegate(RequestCallback);

                RequestThread wt = new RequestThread(this, requestDone, URL, generalSettings.UserAgent, generalSettings.HTTPHeader);
                Thread t = new Thread(new ThreadStart(wt.RunProcess));
                t.IsBackground = true; //make them a daemon - prevent thread callback issues
                t.Start();
            }

        }

        private void GetDeparturesThread()
        {
            Departure.Webservice.Methods.GetDeparturesInput input = new Departure.Webservice.Methods.GetDeparturesInput();

            input.Station = stationSettings.Station;
            input.StationID = stationSettings.StationID;

            input.Transfers = stationSettings.Transfers;

            input.TransfersSelected = stationSettings.TransfersSelected;

            input.UseTrainsFilter = stationSettings.UseTrainsFilter;
            input.TrainsFilter = stationSettings.TrainsFilter;

            input.UseDirectionsFilter = stationSettings.UseDirectionsFilter;
            input.DirectionsFilter = stationSettings.DirectionsFilter;

            input.UsePlatformsFilter = stationSettings.UsePlatformsFilter;
            input.PlatformsFilter = stationSettings.PlatformsFilter;

            ws.setURL(generalSettings.WebServiceURL);

            try
            {

                Invoking.SetText(labelProgressStation, "Getting Departures...");

                string xmlDepartures = ws.GetDepartures(generalSettings.DeviceID, input);
                
                StringReader xmlIn = new StringReader(xmlDepartures);

                dt.Rows.Clear();
                dt.ReadXml(xmlIn);

                int i = 0;

                foreach (DataRow dr in dt.Rows)
                {
                    Invoking.SetText(labelProgressStation, string.Format("Getting Departures ({0}/{1})", i++, dt.Rows.Count));
                    AddStationItem(dr["Time"].ToString(), dr["Train"].ToString(), dr["Direction"].ToString(), dr["Platform"].ToString(), dr["Later"].ToString(), dr["Full_DateTime"].ToString());
                }
            }
            catch (Exception ex)
            {
                Invoking.SetCursor(this, Cursors.Default);

                MessageBox.Show(ex.Message, "An error occured");

            }

            Invoking.SetText(labelProgressStation, "");
            Invoking.SetControlVisible(labelProgressStation, false);
            Invoking.SetControlLayoutMode(this.panelStationItems, false);
            Invoking.SetControlVisible(this.panelStationItems, true);
            isUpdating = false;

            Invoking.SetCursor(this, Cursors.Default);

            if (OnSetRefreshButton != null)
                OnSetRefreshButton(this.Name, true);
        }

        private void GetDeparturesCallback(IAsyncResult asyncResult)
        {
            Invoking.SetControlVisible(this.panelStationItems, false);
            Invoking.SetControlLayoutMode(this.panelStationItems, true);
            Invoking.SetText(labelProgressStation, "Getting Departures...");

            try
            {
                ws = (Webservice.WebserviceWrapper)asyncResult.AsyncState;
                string xmlDepartures = ws.EndGetDepartures(asyncResult);

                StringReader xmlIn = new StringReader(xmlDepartures);
                dt.ReadXml(xmlIn);

                int i = 0;

                foreach (DataRow dr in dt.Rows)
                {
                    Invoking.SetText(labelProgressStation, string.Format("Getting Departures ({0}/{1})", i++, dt.Rows.Count));
                    AddStationItem(dr["Time"].ToString(), dr["Train"].ToString(), dr["Direction"].ToString(), dr["Platform"].ToString(), dr["Later"].ToString(), dr["Full_DateTime"].ToString());
                }
            }
            catch (Exception ex)
            {
                Invoking.SetCursor(this, Cursors.Default);

                MessageBox.Show(ex.Message, "An error occured");

            }

            Invoking.SetText(labelProgressStation, "");
            Invoking.SetControlVisible(labelProgressStation, false);
            Invoking.SetControlVisible(this.panelStationItems, true);
            Invoking.SetControlLayoutMode(this.panelStationItems, false);
            Invoking.RefreshControl(this);
            isUpdating = false;

            Invoking.SetCursor(this, Cursors.Default);

            if (OnSetRefreshButton != null)
                OnSetRefreshButton(this.Name, true);
        }

        private void RequestCallback(string Source, bool Error)
        {

            if (Error == false)
            {
                BuildData(Source);

            }
            else
            {
                labelProgressStation.Text = "";
                labelProgressStation.Visible = false;
                isUpdating = false;
                this.panelStationItems.ResumeLayout(true);
                this.panelStationItems.Visible = true;
                Cursor.Current = Cursors.Default;
                MessageBox.Show(Source, "An error occured");
            }
        }

        private void BuildData(string source)
        {
           
            labelProgressStation.Text = "Filtering Data...";
            labelProgressStation.Refresh();

            BuildDataDelegate buildDataDone = new BuildDataDelegate(BuildDataCallback);

            BuildDataThread wt = new BuildDataThread(this, buildDataDone, source, stationSettings);
            Thread t = new Thread(new ThreadStart(wt.RunProcess));
            t.IsBackground = true; //make them a daemon - prevent thread callback issues
            t.Start();

        }

        private void BuildDataCallback(string Time, string Train, string Direction, string Platform, string Later, string FullDateTime, string Error, bool isError, bool done, bool update, int currentitem, int allitems)
        {
            if (isError)
            {
                labelProgressStation.Text = "";
                labelProgressStation.Visible = false;
                isUpdating = false;
                this.panelStationItems.ResumeLayout(true);
                this.panelStationItems.Visible = true;
                Cursor.Current = Cursors.Default;
                MessageBox.Show(Error, "An error occured");
            }
            else if (done)
            {
                // TODO
                //if (!backgroundWorkerLoadSave.IsBusy)
                //    backgroundWorkerLoadSave.RunWorkerAsync(new object[] { "Save", StationIndex });

               
                labelProgressStation.Text = "";
                labelProgressStation.Visible = false;
                isUpdating = false;
                this.panelStationItems.ResumeLayout(true);
                this.panelStationItems.Visible = true;
                Cursor.Current = Cursors.Default;

                if (OnSetRefreshButton != null)
                    OnSetRefreshButton(this.Name, true);

            }
            else if (update)
            {
                lock (labelProgressStation)
                {
                    labelProgressStation.Text = string.Format("Filtering Trains ({0}/{1})", currentitem, allitems);
                    labelProgressStation.Refresh();
                }
            }
            else
            {
                AddStationItem(Time, Train, Direction, Platform, Later, FullDateTime);
            }

        }

        private void AddStationItem(string Time, string Train, string Direction, string Platform, string Later, string FullDateTime)
        {
            AddStationItem(this.panelStationItems, Time, Train, Direction, Platform, Later, FullDateTime);

            //ctrlStationItem stationItem = new ctrlStationItem(Time, Train, Direction, Platform, Later, FullDateTime, Color.Black);

            //if (Later != "")
            //{
            //    stationItem.Color = Color.Red;
            //}

            //if (Platform == "")
            //    stationItem.Height = 36;
            //else
            //    stationItem.Height = 50;

            //stationItem.Dock = DockStyle.Top;

            //stationItem.OnCountdown += new ctrlStationItem.CountdownEventDelegate(stationItem_OnCountdown);
            
            //this.panelStationItems.Controls.Add(stationItem);
            //this.panelStationItems.Controls.SetChildIndex(stationItem, 0);
        }

        delegate void AddStationItemCallback(Control co, string Time, string Train, string Direction, string Platform, string Later, string FullDateTime);
        public void AddStationItem(Control co, string Time, string Train, string Direction, string Platform, string Later, string FullDateTime)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (co.InvokeRequired)
            {
                AddStationItemCallback d = new AddStationItemCallback(AddStationItem);
                co.Invoke(d, new object[] { co, Time, Train, Direction, Platform, Later, FullDateTime });
            }
            else
            {
                ctrlStationItem stationItem = new ctrlStationItem(Time, Train, Direction, Platform, Later, FullDateTime, Color.Black);

                if (Later != "")
                {
                    stationItem.Color = Color.Red;
                }

                if (Platform == "")
                    stationItem.Height = 36;
                else
                    stationItem.Height = 50;

                stationItem.Dock = DockStyle.Top;

                stationItem.OnCountdown += new ctrlStationItem.CountdownEventDelegate(stationItem_OnCountdown);
                stationItem.OnControlMove += new ctrlStationItem.ControlMoveEventDelegate(stationItem_OnControlMove);

                co.Controls.Add(stationItem);
                co.Controls.SetChildIndex(stationItem, 0);

            }
        }

        void stationItem_OnControlMove(int Y)
        {
            panelStationItems.AutoScrollPosition = new Point(0, -panelStationItems.AutoScrollPosition.Y - Y);
        }

        void stationItem_OnCountdown(string time, string train, string direction, string platform, string later, string fullDateTime)
        {
            string trainDisplay = "";

            trainDisplay = generalSettings.CountdownTrainFormat.Replace("%TI%", time).Replace("%TR%", train).Replace("%DI%", direction).Replace("%PL%", platform);

            if (OnSetCountdown != null)
                OnSetCountdown(fullDateTime, trainDisplay, later);
        }
        
        private void backgroundWorkerLoad_DoWork(object sender, DoWorkEventArgs e)
        {
            LoadDeparturesThread();
        }
        
        #endregion Private Methods

        

    }
}
